local blaze = require "blaze"
local game = require "club.threecard.sudoku"
local gamelib = require "club.threecard.gamelib"
local const = game.util.const

local TimeSpan = CS.System.TimeSpan

local HOUSES = game.config.houses

local M = {}

local log = blaze.logging.get("ingame")

M.state = {
    game_begin_time = 0, -- 游戏开始时间
    marking = false, -- 是否处于做笔记状态
    board_size = 0, -- 面板的大小4、6、9宫
    phase_id = 0, -- 房间阶段编号 0=空闲 1=准备 2=解谜 3=结算
    selected_cell_index = nil,
    cells = {
        -- { index = 0, x = 0, y = 0, value = 1, candidates = { [2] = true, [3] = true}, house_highlight = false },
        -- { index = 1, x = 1, y = 0, value = nil, candidates = {}, house_highlight = false},
    },
    players = {
        -- [player1_id] = {
        --     missed = 0,
        --     max_miss = 0,
        --     total_puzzles = 0,
        --     solved_puzzles = 0,
        --     total_cells = 0,
        --     solved_cells = 0
        -- },
        -- [player2_id] = {
        --     missed = 0,
        --     max_miss = 0,
        --     total_puzzles = 0,
        --     solved_puzzles = 0,
        --     total_cells = 0,
        --     solved_cells = 0
        -- },
    },
    history = {
        -- { action = "erase", args = {} },
        -- { action = "mark", args = {} },
        -- { action = "unmark", args = {} }
    }
}

local function get_cell_by_pos(state, x, y)
    local index = y * state.board_size + x
    local cell = state.cells[index + 1]
    return cell
end

local function clone_candidates(candidates)
    ret = {}
    for k, v in pairs(candidates) do
        ret[k] = v
    end
    return ret
end

M.getters = {
    game_elapsed = function(state)
        -- 已消耗的时间（TimeSpan）
        if state.game_begin_time == 0 then
            return TimeSpan.Zero
        end
        local delta = os.time() - state.game_begin_time
        if delta <= 0 then
            return TimeSpan.Zero
        end
        return TimeSpan.FromSeconds(delta)
    end,
    selected_cell = function(state)
        if not state.selected_cell_index then
            return 
        end
        return state.cells[state.selected_cell_index + 1]
    end,
    can_leave_game = function(state)
        if state.phase_id == 0 or state.phase_id == 3 then
            return true
        end

        local _, roomconfig = game.roomcache.get(gamelib.game_scene.room_config_id)
        if roomconfig.mode == "火拼" then
            return false
        end

        return true
    end
}

M.actions = {
    -- 提示
    hint = function(store, state, args)
        -- 优先使用道具
        local inventory = game.kbe.player().inventory
        if inventory:has(const.ITEM_HINT_ID) then
            return inventory:use(const.ITEM_HINT_ID)
        end

        -- 有广告就看广告提示
        if game.ads.ready() then
            return game.ads.show()
            :next(function()
                return game.kbe.player():hint()
            end)
        end

        -- 没有广告就提示失败
        return blaze.promise.reject("广告还没有准备好")
    end
}

M.mutations = {
    -- 玩家进入
    -- args SudokuPlayer
    player_enter = function(store, state, args)
        store.emit("玩家进入", args)
    end,

    -- 玩家离开
    -- args SudokuPlayer
    player_leave = function(store, state, args)
        store.emit("玩家离开", args)
    end,

    -- 初始化游戏
    -- args.board 面板
    -- args.phase 游戏阶段
    init_game = function(store, state, args)
        store.init_board({ cells = args.board })
        store.set_phase(args.phase)
    end,

    -- 初始化谜题面板
    -- args.cells 所有的格子
    init_board = function(store, state, args)
        local board_size = math.floor(math.sqrt(#args.cells))
        local cells = {}
        for i, value in ipairs(args.cells) do
            local cell = {
                index = i - 1,
                x = (i - 1) % board_size,
                y = math.floor((i - 1) / board_size),
                value = value,
                candidates = {}
            }
            cells[#cells + 1] = cell
        end
        state.board_size = board_size
        state.cells = cells
        store.emit("初始化面板")
    end,

    -- 清理
    clear = function(store, state, args)
        state.game_begin_time = 0
        state.board_size = 0
        state.phase_id = 0
        state.selected_cell_index = nil
        state.cells = {}
        state.players = {}
        store.set_marking(false)
        store.emit("清理")
    end,

    -- 设置游戏开始的时间点
    set_begin_time = function(store, state, args)
        state.game_begin_time = args
    end,

    -- 设置玩家谜题进度
    -- args SudokuPlayer
    set_progress = function(store, state, player)
        state.players[player.id] = player
        store.emit("谜题进度变更", player)
    end,

    set_marking = function(store, state, on)
        state.marking = on
        store.emit("笔记状态变更", on)
    end,

    -- 在指定格子中填入数字
    -- args.x 横坐标
    -- args.y 纵坐标
    -- args.value 数字
    solve = function(store, state, args)
        local cell = get_cell_by_pos(state, args.x, args.y)
        if not cell then
            log.error("解答失败，格子不存在：(%s, %s)", args.x, args.y)
            return
        end
        if cell.value and cell.value > 0 then
            log.warn(string.format("格子里已经有数字了：(%s, %s)=%s", args.x, args.y, cell.value))
            return
        end
        cell.value = args.value
        cell.error = nil
        game.kbe.player():solve(cell.index, args.value)
        store.emit("填入数字", cell)
    end,

    -- 解答成功
    -- args.index 格子的索引
    -- args.value 正确的数字
    set_answer_resolved = function(store, state, args)
        local cell = state.cells[args.index + 1]
        cell.value = args.value
        cell.error = nil
        store.emit("解答正确", cell)
        for k, v in pairs(cell.candidates) do
            if v then
                cell.candidates = {}
                store.emit("标记数字变更", cell)
                return
            end
        end
    end,

    -- 解答失败
    -- args.index 格子的索引
    set_answer_rejected = function(store, state, args)
        local cell = state.cells[args.index + 1]
        local wrong = cell.value
        cell.value = nil
        cell.error = wrong
        store.emit("解答错误", cell)
    end,

    -- 切换备选数字
    toggle_mark = function(store, state, args)
        local cell = get_cell_by_pos(state, args.x, args.y)
        if not cell then
            log.error("标记失败，格子不存在：(%s, %s)", args.x, args.y)
            return
        end
        if cell.candidates[args.value] then
            store.unmark(args)
        else
            store.mark(args)
        end
    end,

    -- 标记某个格子的备选数字
    -- args.x 横坐标
    -- args.y 纵坐标
    -- args.value 备选数字
    mark = function(store, state, args)
        local cell = get_cell_by_pos(state, args.x, args.y)
        if not cell then
            log.error("标记失败，格子不存在：(%s, %s)", args.x, args.y)
            return
        end
        if cell.value then
            -- 格子有值了就不需要标记了
            return
        end
        cell.candidates[args.value] = true
        table.insert(state.history, {
            action = "mark",
            index = cell.index,
            value = args.value
        })
        store.emit("标记数字变更", cell)
    end,

    -- 取消标记某个格子的备选数字
    -- args.x 横坐标
    -- args.y 纵坐标
    -- args.value 备选数字
    unmark = function(store, state, args)
        local cell = get_cell_by_pos(state, args.x, args.y)
        if not cell then
            log.error("取消标记失败，格子不存在：(%s, %s)", args.x, args.y)
            return
        end
        if cell.value then
            -- 格子有值了就不需要标记了
            return
        end
        cell.candidates[args.value] = nil
        table.insert(state.history, {
            action = "unmark",
            index = cell.index,
            value = args.value
        })
        store.emit("标记数字变更", cell)
    end,

    -- 设置当前游戏阶段
    -- args.id 阶段编号
    -- args.countdown 倒计时
    set_phase = function(store, state, args)
        state.phase_id = args.id
        store.emit("游戏阶段变更")
    end,

    -- 选中指定格子
    -- args.x 横坐标
    -- args.y 纵坐标
    select = function(store, state, args)
        local cell = get_cell_by_pos(state, args.x, args.y)
        if not cell then
            log.warn(string.format("选择了一个不存在的格子？(%s, %s)", args.x, args.y))
            return
        end
        if state.selected_cell_index == cell.index then
            return
        end

        if state.selected_cell_index then
            local last_cell = state.cells[state.selected_cell_index + 1]
            last_cell.selected = false
            store.emit("取消选中格子", last_cell)
        end

        state.selected_cell_index = cell.index
        cell.selected = true
        store.emit("选中格子", cell)

        local house_map = HOUSES.MAP[state.board_size][cell.index + 1]
        for i, cell in ipairs(state.cells) do
            local old = cell.house_highlight
            local new = house_map[i]
            if old ~= new then
                cell.house_highlight = new
                store.emit("格子样式变更", cell)
            end
        end
    end,

    -- 擦除当前选中的格子内的所有备选数字
    erase = function(store, state, args)
        local selected_cell = state.selected_cell
        if not selected_cell then
            return
        end
        if selected_cell.value then
            -- 格子有值了就不需要擦除了
            return
        end
        table.insert(state.history, {
            action = "erase",
            index = selected_cell.index,
            candidates = clone_candidates(selected_cell.candidates)
        })
        selected_cell.candidates = {}
        store.emit("标记数字变更", selected_cell)
    end,
    
    -- 撤销上一个操作
    undo = function(store, state, args)
        if #state.history == 0 then
            return
        end
        local last = state.history[#state.history]
        store.undo_action(last)
        state.history[#state.history] = nil
    end,

    undo_action = function(store, state, args)
        if args.action == "erase" then
            local cell = state.cells[args.index + 1]
            cell.candidates = args.candidates
            store.emit("标记数字变更", cell)
        elseif args.action == "mark" then
            local cell = state.cells[args.index + 1]
            cell.candidates[args.value] = false
            store.emit("标记数字变更", cell)
        elseif args.action == "unmark" then
            local cell = state.cells[args.index + 1]
            cell.candidates[args.value] = true
            store.emit("标记数字变更", cell)
        end
    end
}

return M